/*
 * Copyright (c) 2010, Brookhaven National Laboratory
 * All rights reserved.
 * The software in this package is published under the terms
 * of the BSD license a copy of which has been included with
 * this distribution in the license.txt file.
 */


package org.oxtypes.protobuf.impl;

import java.io.IOException;
import java.util.List;
import java.util.LinkedList;

import com.google.protobuf.Descriptors.*;
import com.google.protobuf.WireFormat.*;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;

import org.omg.dds.type.dynamic.DynamicData;

/**
 *
 */
public class PackedFieldBuilder {
    
    public PackedFieldBuilder(){      
    }

    public void readFieldNoTag(CodedInputStream input, int id,
            FieldDescriptor fd, DynamicData dd) throws IOException {

        int byteSize = input.readRawVarint32();
        int limit = input.pushLimit(byteSize);

        switch(fd.getLiteType()){
            // fixed size types
            case DOUBLE  : readDoubles(input, byteSize, id, dd); break;
            case FLOAT   : readFloats(input, byteSize, id, dd); break;
            case FIXED64 : readFixed64s(input, byteSize, id, dd); break;
            case FIXED32 : readFixed32s(input, byteSize, id, dd); break;
            case SFIXED64: readSFixed64s(input, byteSize, id, dd); break;
            case SFIXED32: readSFixed32s(input, byteSize, id, dd); break;
            // variable size types
            case INT64   : readInt64s(input, byteSize, id, dd); break;
            case INT32   : readInt32s(input, byteSize, id, dd); break;
            case UINT64  : readUInt64s(input, byteSize, id, dd); break;
            case UINT32  : readUInt32s(input, byteSize, id, dd); break;
            case SINT64  : readSInt64s(input, byteSize, id, dd); break;
            case SINT32  : readSInt32s(input, byteSize, id, dd); break;
            case BOOL    : readBools(input, byteSize, id, dd); break;
            case ENUM    : readEnums(input, byteSize, id, dd); break;
            default:
              throw new IllegalArgumentException("type of the packed field is not primitive");
 
        }

        input.popLimit(limit);
    }

    private void readDoubles(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        int length = byteSize/doubleSize;
        double values[] = new double[length];

        int counter = 0;
        while (input.getBytesUntilLimit() > 0) {
            values[counter++] = input.readDouble();
        }

        if(counter != length) throw new RuntimeException("counter != length");

        dd.setFloat64Values(id, values, 0, length);
    }

    private void readFloats(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        int length = byteSize/floatSize;
        float values[] = new float[length];

        int counter = 0;
        while (input.getBytesUntilLimit() > 0) {
            values[counter++] = input.readFloat();
        }

        if(counter != length) throw new RuntimeException("counter != length");

        dd.setFloat32Values(id, values, 0, length);
    }

    private void readFixed64s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        int length = byteSize/fixed64Size;
        long values[] = new long[length];

        int counter = 0;
        while (input.getBytesUntilLimit() > 0) {
            values[counter++] = input.readFixed64();
        }

        if(counter != length) throw new RuntimeException("counter != length");

        dd.setInt64Values(id, values, 0, length);
    }

    private void readFixed32s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        int length = byteSize/fixed32Size;
        int values[] = new int[length];

        int counter = 0;
        while (input.getBytesUntilLimit() > 0) {
            values[counter++] = input.readFixed32();
        }

        if(counter != length) throw new RuntimeException("counter != length");

        dd.setInt32Values(id, values, 0, length);
    }

    private void readSFixed64s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        int length = byteSize/sfixed64Size;
        long values[] = new long[length];

        int counter = 0;
        while (input.getBytesUntilLimit() > 0) {
            values[counter++] = input.readSFixed64();
        }

        if(counter != length) throw new RuntimeException("counter != length");

        dd.setInt64Values(id, values, 0, length);
    }

    private void readSFixed32s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        int length = byteSize/sfixed32Size;
        int values[] = new int[length];

        int counter = 0;
        while (input.getBytesUntilLimit() > 0) {
            values[counter++] = input.readSFixed32();
        }

        if(counter != length) throw new RuntimeException("counter != length");

        dd.setInt32Values(id, values, 0, length);
    }

    private void readInt64s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        List<Long> longList = new LinkedList<Long>();

        while (input.getBytesUntilLimit() > 0) {
            longList.add(input.readInt64());
        }

        long values[] = new long[longList.size()];

        int counter = 0;
        for(Long value: longList){
            values[counter++] = value.longValue();
        }

        dd.setInt64Values(id, values, 0, values.length);
    }

    private void readInt32s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        List<Integer> intList = new LinkedList<Integer>();

        while (input.getBytesUntilLimit() > 0) {
            intList.add(input.readInt32());
        }

        int values[] = new int[intList.size()];

        int counter = 0;
        for(Integer value: intList){
            values[counter++] = value.intValue();
        }

        dd.setInt32Values(id, values, 0, values.length);
    }

    private void readUInt64s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        List<Long> longList = new LinkedList<Long>();

        while (input.getBytesUntilLimit() > 0) {
            longList.add(input.readUInt64());
        }

        long values[] = new long[longList.size()];

        int counter = 0;
        for(Long value: longList){
            values[counter++] = value.longValue();
        }

        dd.setInt64Values(id, values, 0, values.length);
    }

    private void readUInt32s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        List<Integer> intList = new LinkedList<Integer>();

        while (input.getBytesUntilLimit() > 0) {
            intList.add(input.readUInt32());
        }

        int values[] = new int[intList.size()];

        int counter = 0;
        for(Integer value: intList){
            values[counter++] = value.intValue();
        }

        dd.setInt32Values(id, values, 0, values.length);
    }

     private void readSInt64s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        List<Long> longList = new LinkedList<Long>();

        while (input.getBytesUntilLimit() > 0) {
            longList.add(input.readSInt64());
        }

        long values[] = new long[longList.size()];

        int counter = 0;
        for(Long value: longList){
            values[counter++] = value.longValue();
        }

        dd.setInt64Values(id, values, 0, values.length);
    }

    private void readSInt32s(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        List<Integer> intList = new LinkedList<Integer>();

        while (input.getBytesUntilLimit() > 0) {
            intList.add(input.readSInt32());
        }

        int values[] = new int[intList.size()];

        int counter = 0;
        for(Integer value: intList){
            values[counter++] = value.intValue();
        }

        dd.setInt32Values(id, values, 0, values.length);
    }

    private void readBools(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        List<Boolean> boolList = new LinkedList<Boolean>();

        while (input.getBytesUntilLimit() > 0) {
            boolList.add(input.readBool());
        }

        boolean values[] = new boolean[boolList.size()];

        int counter = 0;
        for(Boolean value: boolList){
            values[counter++] = value.booleanValue();
        }

        dd.setBooleanValues(id, values, 0, values.length);
    }

    private void readEnums(CodedInputStream input, int byteSize,
            int id, DynamicData dd) throws IOException {

        List<Integer> intList = new LinkedList<Integer>();

        while (input.getBytesUntilLimit() > 0) {
            intList.add(input.readEnum());
        }

        int values[] = new int[intList.size()];

        int counter = 0;
        for(Integer value: intList){
            values[counter++] = value.intValue();
        }

        dd.setInt32Values(id, values, 0, values.length);
    }

    static private int doubleSize = CodedOutputStream.computeDoubleSizeNoTag(0.0);
    static private int floatSize = CodedOutputStream.computeFloatSizeNoTag(0);
    static private int fixed64Size = CodedOutputStream.computeFixed64SizeNoTag(0);
    static private int fixed32Size = CodedOutputStream.computeFixed32SizeNoTag(0);
    static private int sfixed64Size = CodedOutputStream.computeSFixed64SizeNoTag(0);
    static private int sfixed32Size = CodedOutputStream.computeSFixed32SizeNoTag(0);
}
